#ifndef MLOADER_TEXTURE
#define MLOADER_TEXTURE

#include "Common.h"

namespace MLoader
{
    class Texture2D
    {
    public:
        typedef enum TexFormat
        {
            R8          = 0x0,
            R8_G8       = 0x1,
            R8_G8_B8    = 0x2,
            R8_G8_B8_A8 = 0x3
        } TexFormat;
        
        Texture2D() = delete;
        Texture2D(const int& width, const int& height, const TexFormat& format, const int& index = 0)
        {
            int per_pixel_size = 8;
            switch (format) {
                case R8:            per_pixel_size =  8; break;
                case R8_G8:         per_pixel_size = 16; break;
                case R8_G8_B8:      per_pixel_size = 24; break;
                case R8_G8_B8_A8:   per_pixel_size = 32; break;
                default:            per_pixel_size =  8; break;
            }
            
            RawData = (uint8_t *)malloc(width * height * per_pixel_size);
            ID = Hash(width, height, format, index);
            Enable(true);
        }
        ~Texture2D()
        {
            if (RawData) {
                free(RawData);
                RawData = nullptr;
            }
        }
        
        void Enable(bool isActive) {
            if (isActive) {
                ID |= (0x1 << 31);
            } else {
                ID &= 0x7FFFFFFF;
            }
        }
        inline bool IsActive() {
            return (ID >> 31) & 0x1;
        }

        // MAX 4095
        inline int Width() {
            return ID & 0xFFF;
        }
        // MAX 4095
        inline int Height() {
            return (ID >> 12) & 0xFFF;
        }
        inline TexFormat Format() {
            return TexFormat((ID >> 24) & 0xF);
        }
        
        inline uint8_t* Data() {
            return RawData;
        }
        
    public:
        static inline uint32_t Hash(const int& width, const int& height, const TexFormat& format, const int& index) {
            uint32_t code = 0;
            code |= width;
            code |= (height << 12);
            code |= (format << 24);
            code |= (index  << 28);
            
            return code;
        }

        static Texture2D* CreateTexture2D(const int& width, const int& height, const TexFormat& format)
        {
            int index = GetUnusedIndex(width, height, format);
            
            if (-1 != index) {
                Texture2D* t = new Texture2D(width, height, format, index);
                Texture2DCache.emplace(t->ID, t);
                return t;
            } else {
                return nullptr;
            }
        }
        
        static Texture2D* GetTexture2D(const int& width, const int& height, const TexFormat& format)
        {
            int index = GetUnusedIndex(width, height, format);
            if (-1 == index) {
                return nullptr;
            } else if (0 == index) {
                Texture2D* t = new Texture2D(width, height, format, 0);
                Texture2DCache.emplace(t->ID, t);
                return t;
            } else {
                return Texture2DCache[Hash(width, height, format, index - 1)];
            }
        }
        
        static void ClearTexture2DCache()
        {
            Texture2DCache.clear();
        }
        
    private:
        // 31       | 30 - 28 | 27 - 24   | 23 - 12 | 11 - 0
        // isActive | index   | TexFormat | height  | width
        uint32_t ID;
        uint8_t* RawData;
        static std::unordered_map<uint32_t, Texture2D*> Texture2DCache;
        
    private:
        static int GetUnusedIndex(const int& width, const int& height, const TexFormat& format) {
            int index = 0;
            uint32_t code = Hash(width, height, format, index);
            while (Texture2DCache.find(code) != Texture2DCache.end()) {
                ++index;
                
                if (index > 7) {
                    ML_LOG("Error! No more textures!");
                    return -1;
                }
                
                code = Hash(width, height, format, index);
            }
            
            return index;
        }
        
        // MAX 7
        inline int Index() {
            return (ID >> 28) & 0xFFF;
        }
    };
}


#endif // MLOADER_TEXTURE
